package org.hackystat.devcathlon.engine.event.impl;

import static org.junit.Assert.assertEquals;
import javax.xml.datatype.XMLGregorianCalendar;
import org.hackystat.devcathlon.configuration.ConfigurationManager;
import org.hackystat.devcathlon.engine.event.Event;
import org.hackystat.devcathlon.engine.event.EventScore;
import org.hackystat.devcathlon.engine.match.Match;
import org.hackystat.devcathlon.engine.team.Team;
import org.hackystat.devcathlon.engine.test.DevcathlonTestHelper;
import org.hackystat.sensorbase.resource.projects.jaxb.Project;
import org.hackystat.utilities.tstamp.Tstamp;
import org.junit.Test;
import org.junit.BeforeClass;

/**
 * Tests the FixTheBuild event.
 * 
 * @author John Ancheta
 */
public class TestFixTheBuild extends DevcathlonTestHelper {
  private String teamName = "TestTeam";
  private String teamOwner = "TeamOwner";
  private String matchName = "TestMatch";
  private String member1 = "Member1";
  private String member2 = "Member2";

  private static int baseScoreWeight = 1;
  private static int wakeupInterval = 60;
  private static int numberFailedBuildsMin = 3;
  private static int numberFailedBuildsMax = 6;
  private static int numberFailedBuildsPenalty = -5;
  private static int largeNumberFailedBuilds = 7;
  private static int largeNumberFailedBuildsPenalty = -10;
  private static int noScore = 0;
  /**
   * Sets up the DevcathlonConfiguration for use in these test cases. 
   * 
   * @throws Exception If problems occur setting up the configuration. 
   */
  @BeforeClass
  public static void setupConfiguration() throws Exception {
    // Set up the testing DevcathlonConfiguration for the event FixTheBuild.
    String eventName = FixTheBuild.eventName;
    ConfigurationManager configManager = ConfigurationManager.getInstance();
    configManager.setBaseScoreWeight(eventName, baseScoreWeight);
    configManager.getConfiguration(eventName);
    configManager.setWakeupInterval(eventName, wakeupInterval);
    configManager.addProperty(eventName, FixTheBuild.numberFailedBuildsMinKey, 
                              Integer.toString(numberFailedBuildsMin));
    configManager.addProperty(eventName, FixTheBuild.numberFailedBuildsMaxKey, 
                              Integer.toString(numberFailedBuildsMax));
    configManager.addProperty(eventName, FixTheBuild.numberFailedBuildsPenaltyKey, 
                              Integer.toString(numberFailedBuildsPenalty));
    configManager.addProperty(eventName, FixTheBuild.largeNumberFailedBuildsKey, 
                              Integer.toString(largeNumberFailedBuilds));
    configManager.addProperty(eventName, FixTheBuild.largeNumberFailedBuildsPenaltyKey, 
                              Integer.toString(largeNumberFailedBuildsPenalty));
  }
  
  /**
   * Tests configuration values.
   *
   * @throws Exception Unexpected exception.
   */
  @Test
  public void testConfigurationValues() throws Exception {
    FixTheBuild fixTheBuild = new FixTheBuild();
    assertEquals("Check BaseScoreWeight", baseScoreWeight, fixTheBuild.getBaseScoreWeight());
    assertEquals("Check WakeupInterval", wakeupInterval, fixTheBuild.getWakeupInterval());
    assertEquals("Check numberFailedBuildsMinKey", numberFailedBuildsMin,
                 fixTheBuild.getNumberFailedBuildsMin());
    assertEquals("Check numberFailedBuildsMaxKey", numberFailedBuildsMax,
                 fixTheBuild.getNumberFailedBuildsMax());
    assertEquals("Check numberFailedBuildsPenalty", numberFailedBuildsPenalty,
                 fixTheBuild.getNumberFailedBuildsPenalty());
    assertEquals("Check numberFailedBuildsPenalty", largeNumberFailedBuilds,
                 fixTheBuild.getLargeNumberFailedBuilds());
    assertEquals("Check numberFailedBuildsPenalty", largeNumberFailedBuildsPenalty,
                 fixTheBuild.getLargeNumberFailedBuildsPenalty());
  }

  /**
   * Tests this event when there is no sensor data for this team.
   *
   * @throws Exception Unexpected exception.
   */
  @Test
  public void testNoBuildSensorData() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new FixTheBuild();
    
    // Create a simple match containing a single team, a single project, and this event.
    Match match = addSimpleMatch(matchName, team, getProject(teamOwner, teamName), event);
    
    // Test the event with no sensor data.
    EventScore score = event.computeScore(match, team, Tstamp.makeTimestamp());
    assertEquals("For not builds, there should be no penalties", noScore, 
                 event.computePoints(score));
    
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
    
  /**
   * Tests event scores for less than NumberFailedBuildsMin. 
   *
   * @throws Exception Unexpected exception.
   */
  @Test
  public void testSmallBuildFailures() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new FixTheBuild();
    
    // Create a simple match containing a single team, a single project, and this event.
    Project project = getProject(teamOwner, teamName);
    Match match = addSimpleMatch(matchName, team, project, event);
    
    XMLGregorianCalendar timestamp = Tstamp.makeTimestamp();
    for (int i = 1; i <= numberFailedBuildsMin; i++) {
      makeBuildData(team.getOwner(), project, Tstamp.incrementMinutes(timestamp, i), false);
    }
    
    EventScore score = event.computeScore(match, team, Tstamp.incrementMinutes(timestamp,
                                          wakeupInterval));
    assertEquals("Should be NumberFailedBuildsPenalty.", 
                 numberFailedBuildsPenalty, event.computePoints(score));
 
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
  
  /**
   * Tests this event score when the previous interval had 
   * LargeNumberFailedBuilds.
   *
   * @throws Exception Unexpected exception.
   */
  @Test
  public void testLargeFailedBuilds() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new FixTheBuild();
    
    // Create a simple match containing a single team, a single project, and this event.
    Project project = getProject(teamOwner, teamName);
    Match match = addSimpleMatch(matchName, team, project, event);
    
    XMLGregorianCalendar timestamp = Tstamp.makeTimestamp();
    for (int i = 1; i <= largeNumberFailedBuilds; i++) {
      makeBuildData(team.getOwner(), project, Tstamp.incrementMinutes(timestamp, i), false);
    }

    EventScore score = event.computeScore(match, team, Tstamp.incrementMinutes(timestamp,
                                          wakeupInterval));
    assertEquals("Large number of \"Failure\" builds.",
                 largeNumberFailedBuildsPenalty, 
                 event.computePoints(score));
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
  
  /**
   * Tests this event score when the previous interval had 
   * NumberFailedBuildsMax.
   *
   * @throws Exception Unexpected exception.
   */
  @Test
  public void testFailedBuilds() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new FixTheBuild();
    
    // Create a simple match containing a single team, a single project, and this event.
    Project project = getProject(teamOwner, teamName);
    Match match = addSimpleMatch(matchName, team, project, event);
    
    XMLGregorianCalendar timestamp = Tstamp.makeTimestamp();
    for (int i = 1; i <= numberFailedBuildsMax; i++) {
      makeBuildData(team.getOwner(), project, Tstamp.incrementMinutes(timestamp, i), false);
    }

    EventScore score = event.computeScore(match, team, Tstamp.incrementMinutes(timestamp,
                                          wakeupInterval));
    assertEquals("Number of \"Failure\" builds.",
                 numberFailedBuildsPenalty, 
                 event.computePoints(score));
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
  
  /**
   * Tests this event score when the previous interval had 
   * LargeNumberFailedBuilds.
   *
   * @throws Exception Unexpected exception.
   */
  @Test
  public void testDecay() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new FixTheBuild();
    
    // Create a simple match containing a single team, a single project, and this event.
    Project project = getProject(teamOwner, teamName);
    Match match = addSimpleMatch(matchName, team, project, event);
    
    XMLGregorianCalendar timestamp = Tstamp.makeTimestamp();
    for (int i = 1; i <= largeNumberFailedBuilds; i++) {
      XMLGregorianCalendar time = Tstamp.incrementMinutes(timestamp, i);
      makeBuildData(team.getOwner(), project, time, false);
    }
    
    int start = wakeupInterval + 1;
    int end = wakeupInterval + numberFailedBuildsMax;
    for (int i = start; i <= end; i++) {
      XMLGregorianCalendar time = Tstamp.incrementMinutes(timestamp, i);
      makeBuildData(team.getOwner(), project, time, false);
    }

    XMLGregorianCalendar time = Tstamp.incrementMinutes(timestamp, 2 * wakeupInterval);
    EventScore score = event.computeScore(match, team, time);
    assertEquals("Number of \"Failure\" builds.",
                 noScore, event.computePoints(score));
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
  
  /**
   * Tests this event score when the previous two intervals had large
   * number of failed builds.
   *
   * @throws Exception Unexpected exception.
   */
  @Test
  public void testNoDecay() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new FixTheBuild();
    
    // Create a simple match containing a single team, a single project, and this event.
    Project project = getProject(teamOwner, teamName);
    Match match = addSimpleMatch(matchName, team, project, event);
    
    XMLGregorianCalendar timestamp = Tstamp.makeTimestamp();
    
    int start = 1;
    int end = start + largeNumberFailedBuilds;
    for (int i = start; i <= end; i++) {
      XMLGregorianCalendar time = Tstamp.incrementMinutes(timestamp, i);
      makeBuildData(team.getOwner(), project, time, false);
    }
    
    start = wakeupInterval + 1;
    end = wakeupInterval + largeNumberFailedBuilds;
    for (int i = start; i <= end; i++) {
      XMLGregorianCalendar time = Tstamp.incrementMinutes(timestamp, i);
      makeBuildData(team.getOwner(), project, time, false);
    }
    
    start = 2 * wakeupInterval + 1;
    end = 2 * wakeupInterval + largeNumberFailedBuilds;
    for (int i = start; i <= end; i++) {
      XMLGregorianCalendar time = Tstamp.incrementMinutes(timestamp, i);
      makeBuildData(team.getOwner(), project, time, false);
    }

    XMLGregorianCalendar time = Tstamp.incrementMinutes(timestamp, 3 * wakeupInterval);
    EventScore score = event.computeScore(match, team, time);
    assertEquals("Number of \"Failure\" builds.",
                 largeNumberFailedBuildsPenalty, event.computePoints(score));
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
}